/**
 * 
 */
package gui.view;

import interfaces.IElement;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.GroupLayout.Alignment;
import javax.swing.GroupLayout.ParallelGroup;
import javax.swing.GroupLayout.SequentialGroup;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;

import joint.JointOperator;
import massDistribution.JointMassDistribution;
import massDistribution.MassDistribution;
import stubs.ServiceStub;
import utilities.DoubleUtility;
import core.FocalElement;
import core.FrameOfDiscernment;
import core.PowerSet;
import engine.Service;

/**
 * @author Elisa Costante
 * 
 */
public class ResultsPanel extends JPanel {

	private static final long serialVersionUID = -8160929473998060365L;

	private ArrayList<JPanel> allPanel;

	private ArrayList<Service> allCandidateServices;
	private Service callerService;

	private Hashtable<ServiceStub, Hashtable<JointOperator, JointMassDistribution>> reputationHashMap;
	private int numberOfPanel;
	private FrameOfDiscernment frameOfDiscernment;
	private PowerSet powerSet;
	private Hashtable<Service, MassDistribution> monitoredMass;

	private Hashtable<Service, ArrayList<MassDistribution>> allAggregateTrustHashMap;

	/**
	 * @param allReputationHashMap
	 */
	public ResultsPanel(
			Hashtable<Service, ArrayList<JointMassDistribution>> allReputationHashMap) {
		super();
		this.reputationHashMap = new Hashtable<ServiceStub, Hashtable<JointOperator, JointMassDistribution>>();
		setJointMassesMap(allReputationHashMap);
	}

	/**
	 * @param jointMassHashMap
	 */
	private void setJointMassesMap(
			Hashtable<Service, ArrayList<JointMassDistribution>> jointMassHashMap) {
		if (jointMassHashMap != null && jointMassHashMap.size() > 0) {
			Enumeration<Service> keysEnum = jointMassHashMap.keys();

			while (keysEnum.hasMoreElements()) {
				// Per ogni servizio
				ServiceStub serviceStub = (ServiceStub) keysEnum.nextElement();
				// Hai un arraylist di jointMass
				ArrayList<JointMassDistribution> joinForServices = jointMassHashMap
						.get(serviceStub);
				if (joinForServices != null) {
					Hashtable<JointOperator, JointMassDistribution> jointOperatorInnerMap = new Hashtable<JointOperator, JointMassDistribution>();
					for (int i = 0; i < joinForServices.size(); i++) {
						JointMassDistribution mass = joinForServices.get(i);
						if (mass != null && mass.getOperator() != null) {

							jointOperatorInnerMap.put(mass.getOperator(), mass);

						} else {
							System.out.println("??");
						}
					}
					this.reputationHashMap.put(serviceStub,
							jointOperatorInnerMap);
				}

			}

		}
	}

	public void initPanel() {
		this.setBackground(CustomizedColor.BACK_GROUND_COLOR);
		allPanel = new ArrayList<JPanel>();
		for (int i = 0; i < numberOfPanel; i++) {
			ServiceStub service = (ServiceStub) allCandidateServices.get(i);
			// Set up a new panel for the services
			JPanel newPanel = setPanel(service);
			allPanel.add(newPanel);
		}

		setMainLayout();

	}

	/**
	 * @param iservice
	 */
	private JPanel setPanel(ServiceStub service) {
		JPanel panel = new JPanel();
		panel.setBackground(CustomizedColor.WHITE);
		Border border = BorderFactory.createLineBorder(Color.gray);
		border = BorderFactory.createTitledBorder(border, service
				.getIdentifierName(), TitledBorder.CENTER, TitledBorder.CENTER);
		panel.setBorder(border);

		setPanelComponents(panel, service);

		return panel;

	}

	/**
	 * @param panel
	 */
	private void setPanelComponents(JPanel panel, ServiceStub service) {

		// FirstLine Components
	
		// SecondLine

		// Set the reputation table
		MyTableModel reputationTableModel = new MyTableModel(service,
				TableType.REPUTATION);
		JTable reputationTable = new JTable(reputationTableModel);
		setFirstColumnWidth(reputationTable);

		MyCustomTableCellRenderer reputationRenderer = new MyCustomTableCellRenderer();
		reputationTable.setDefaultRenderer(String.class, reputationRenderer);

		JScrollPane reputationScroll = new JScrollPane(reputationTable);
		reputationTable.setFillsViewportHeight(true);
		reputationScroll
				.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		reputationScroll
				.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);

		// Set the relationship table
		MyTableModel relationshipTableModel = new MyTableModel(service,
				TableType.RELATIONSHIP);
		JTable relationshipTable = new JTable(relationshipTableModel);
		setFirstColumnWidth(relationshipTable);

		// Set a new Cell Renderer
		MyCustomTableCellRenderer relationRenderer = new MyCustomTableCellRenderer();
		relationshipTable.setDefaultRenderer(String.class, relationRenderer);

		JScrollPane relationshipScroll = new JScrollPane(relationshipTable);
		relationshipTable.setFillsViewportHeight(true);
		relationshipScroll
				.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		relationshipScroll
				.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);

		// Set the Aggregated Table

		MyTableModel aggregateTableModel = new MyTableModel(service,
				TableType.AGGREGATE);
		JTable aggregateJTable = new JTable(aggregateTableModel);
		setFirstColumnWidth(aggregateJTable);

		// Set a new Cell Renderer
		MyCustomTableCellRenderer aggregateRenderer = new MyCustomTableCellRenderer();
		aggregateJTable.setDefaultRenderer(String.class, aggregateRenderer);

		JScrollPane aggregateScroll = new JScrollPane(aggregateJTable);
		aggregateJTable.setFillsViewportHeight(true);
		aggregateScroll
				.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		aggregateScroll
				.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);

		// Set the Layout

		GroupLayout layout = new GroupLayout(panel);
		panel.setLayout(layout);
		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);

		ParallelGroup horizontal = layout.createParallelGroup(Alignment.CENTER);
	

		// Secondline

		SequentialGroup secondLine = layout.createSequentialGroup();
		// secondLine.addComponent(emptySpace, 100, 100, Short.MAX_VALUE);
		secondLine.addComponent(reputationScroll, 100, 100, Short.MAX_VALUE);
		secondLine.addComponent(relationshipScroll, 100, 100, Short.MAX_VALUE);
		secondLine.addComponent(aggregateScroll, 100, 100, Short.MAX_VALUE);

		horizontal.addGroup(secondLine);

		SequentialGroup vertical = layout.createSequentialGroup();

		
		ParallelGroup secundColumn = layout.createParallelGroup();
		// secundColumn.addComponent(emptySpace, 50, 50, Short.MAX_VALUE);
		secundColumn.addComponent(reputationScroll, 50, 50, Short.MAX_VALUE);
		secundColumn.addComponent(relationshipScroll, 50, 50, Short.MAX_VALUE);
		secundColumn.addComponent(aggregateScroll, 50, 50, Short.MAX_VALUE);

		vertical.addGroup(secundColumn);

		layout.setHorizontalGroup(horizontal);
		layout.setVerticalGroup(vertical);

	}

	/**
	 * @param jtable
	 */
	private void setFirstColumnWidth(JTable jtable) {
		// Set the first visible column to 100 pixels wide
		int vColIndex = 0;
		TableColumn col = jtable.getColumnModel().getColumn(vColIndex);
		int width = 100;
		col.setPreferredWidth(width);
	}

	/**
	 * 
	 */
	private void setMainLayout() {

		JLabel callerLabel = new JLabel("Caller: "
				+ callerService.getIdentifierName());
		JLabel reputationLabel = new JLabel("Reputation");
		JLabel relationShiplabel = new JLabel("Relationship");
		JLabel trustLabel = new JLabel("Trust");
		
		GroupLayout layout = new GroupLayout(this);
		this.setLayout(layout);
		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);

		ParallelGroup horizontal = layout.createParallelGroup(Alignment.CENTER);
		SequentialGroup vertical = layout.createSequentialGroup();
		
		horizontal.addComponent(callerLabel, 100, 100, 200);

		vertical.addComponent(callerLabel, 30, 30, 30);
		
		
		SequentialGroup allLabelsHor= layout.createSequentialGroup();
		allLabelsHor.addComponent(reputationLabel,300,300,300);
		allLabelsHor.addComponent(relationShiplabel,400,400,400);
		allLabelsHor.addComponent(trustLabel,300,300,300);
		horizontal.addGroup(allLabelsHor);
		
		ParallelGroup allLabelsVer= layout.createParallelGroup();
		allLabelsVer.addComponent(reputationLabel,30, 30, 30);
		allLabelsVer.addComponent(relationShiplabel,30, 30, 30);
		allLabelsVer.addComponent(trustLabel,30, 30, 30);
		vertical.addGroup(allLabelsVer);
		
		
		for (int i = 0; i < allPanel.size(); i++) {
			horizontal.addComponent(allPanel.get(i), 100, 100, Short.MAX_VALUE);
			vertical.addComponent(allPanel.get(i), 0, 0, Short.MAX_VALUE);

		}

		layout.setHorizontalGroup(horizontal);
		layout.setVerticalGroup(vertical);

	}

	/**
	 * @param allCandidateServices
	 *            the allCandidateServices to set
	 */
	public void setAllCandidateServices(ArrayList<Service> allCandidateServices) {
		this.allCandidateServices = allCandidateServices;
		this.numberOfPanel = allCandidateServices.size();

	}

	/**
	 * @param callerService
	 *            the callerService to set
	 */
	public void setCallerService(Service callerService) {
		this.callerService = callerService;
	}

	/**
	 * @param reputationHashMap
	 *            the reputationHashMap to set
	 */
	public void setReputationHashMap(
			Hashtable<ServiceStub, Hashtable<JointOperator, JointMassDistribution>> reputationHashMap) {
		this.reputationHashMap = reputationHashMap;
	}

	/**
	 * @param frameOfDiscernment
	 *            the frameOfDiscernment to set
	 */
	public void setFrameOfDiscernment(FrameOfDiscernment frameOfDiscernment) {
		this.frameOfDiscernment = frameOfDiscernment;
		this.powerSet = this.frameOfDiscernment.getPowerSet();

	}

	/**
	 * @param monitoredMass
	 *            the monitoredMass to set
	 */
	public void setMonitoredMass(
			Hashtable<Service, MassDistribution> monitoredMass) {
		this.monitoredMass = monitoredMass;
	}

	/**
	 * @param allAggregateTrustHashMap
	 *            the allAggregateTrustHashMap to set
	 */
	public void setAllAggregateTrustHashMap(
			Hashtable<Service, ArrayList<MassDistribution>> allAggregateTrustHashMap) {
		this.allAggregateTrustHashMap = allAggregateTrustHashMap;
	}

	private class MyTableModel extends DefaultTableModel {

		private ServiceStub candidateService;
		private TableType tableType;

		// bestValuesIndex[i] contains the index of the column which have the
		// best value for the j column
		private int[] bestValuesIndex;

		public MyTableModel(ServiceStub candidateService, TableType tableType) {
			super();
			this.candidateService = candidateService;
			this.tableType = tableType;

			setColumnHeader();
			setRowsHeader();
			setData();

		}

		/**
		 * 
		 */
		private void setRowsHeader() {
			if (tableType.equals(TableType.REPUTATION)
					|| tableType.equals(TableType.AGGREGATE)) {
				// Reputationtable
				JointOperator[] allJointOperator = JointOperator.values();
				this.setRowCount(allJointOperator.length);

				for (int i = 0; i < allJointOperator.length; i++) {
					this.setValueAt(allJointOperator[i], i, 0);

				}

			} else if (tableType.equals(TableType.RELATIONSHIP)) {
				// RelationShip table
				this.setRowCount(1);
				this.setValueAt("Monitor", 0, 0);

			}
		}

		/**
		 * 
		 */
		private void setColumnHeader() {

			// compute the number of column
			int numberOfColumn = computeNumberOfColumn();
			this.setColumnCount(numberOfColumn + 1);
			// Set Column NAmes
			String[] columnNames = new String[getColumnCount()];
			columnNames[0] = "";

			ArrayList<IElement> allElements = powerSet.getElements();

			for (int i = 0; i < allElements.size(); i++) {
				if (allElements.get(i).equals(
						frameOfDiscernment.getUniversalSet())) {

					columnNames[i + 1] = new String("\u0398");

				} else if (allElements.get(i).isEmptySet()) {
					columnNames[i + 1] = new String("\u2205");

				} else
					columnNames[i + 1] = allElements.get(i).toString();

			}
			Arrays.sort(columnNames);
			this.setColumnIdentifiers(columnNames);

		}

		/**
		 * @return
		 */
		private int computeNumberOfColumn() {
			// il numero di colonne è dato dal powerSet
			if (powerSet != null && powerSet.size() > 0) {

				return powerSet.size();
			} else
				return 0;

		}

		/**
		 * 
		 */
		private void setData() {
			bestValuesIndex = new int[getRowCount()];

			if (tableType.equals(TableType.REPUTATION)) {
				setReputationTabel();

			} else if (tableType.equals(TableType.RELATIONSHIP)) {
				// RelationShip table
				setRelationshipTable();

			} else if (tableType.equals(TableType.AGGREGATE)) {
				// Aggregation Table
				setAggregationTable();
			}
		}

		/**
		 * 
		 */
		private void setAggregationTable() {
			for (int i = 0; i < getRowCount(); i++) {
				// the best value for this row
				double maxValue = 0.0;

				// Read the column Header
				JointOperator operator = (JointOperator) this.getValueAt(i, 0);
				// Get The Hashtable with couples
				// <JointOperator,JOintMassDistribution>
				ArrayList<MassDistribution> allMassAggregate = allAggregateTrustHashMap
						.get(candidateService);
				for (int j = 0; j < allMassAggregate.size(); j++) {

					MassDistribution aggregateMass = allMassAggregate.get(j);
					if (aggregateMass instanceof JointMassDistribution) {
						JointMassDistribution jointMass = (JointMassDistribution) aggregateMass;
						JointOperator thisOperator = jointMass.getOperator();
						if (operator.equals(thisOperator)) {
							// For each column set the focal element

							for (int k = 1; k < getColumnCount(); k++) {

								String trustElement = getColumnName(k);
								if (trustElement.equals("\u0398")) {
									trustElement = frameOfDiscernment
											.getUniversalSet().toString();
								} else if (trustElement.equals("\u2205")) {
									trustElement = "{}";
								}
								// Set the cell Joint-Operator/TrustClass
								FocalElement focalElement = (FocalElement) jointMass
										.getFocalElement(trustElement);
								if (focalElement != null) {
									double bpa = focalElement.getBpa();
									if (bpa > maxValue) {
										maxValue = bpa;
										bestValuesIndex[i] = k;
									}
									setValueAt(DoubleUtility.doubleToString(
											bpa, 2), i, k);
								} else {
									setValueAt("", i, k);
								}
							}
						}
					} else {
						// The aggregate Trust is just formed from the
						// monitoredMAss
						// TODO

					}

				}
			}
		}

		/**
		 * 
		 */
		private void setRelationshipTable() {
			if (monitoredMass != null) {
				double maxValue = 0.0;
				for (int j = 1; j < getColumnCount(); j++) {

					String trustElement = getColumnName(j);
					if (trustElement.equals("\u0398")) {
						trustElement = frameOfDiscernment.getUniversalSet()
								.toString();
					} else if (trustElement.equals("\u2205")) {
						trustElement = "{}";
					}
					// Retrive the mass distribution for the candidate
					// service
					MassDistribution mass = monitoredMass.get(candidateService);
					// Set the cell Joint-Operator/TrustClass
					FocalElement focalElement = (FocalElement) mass
							.getFocalElement(trustElement);
					if (focalElement != null) {
						double bpa = focalElement.getBpa();
						if (bpa > maxValue) {
							maxValue = bpa;
							bestValuesIndex[0] = j;
						}
						setValueAt(DoubleUtility.doubleToString(bpa, 2), 0, j);
					} else {
						setValueAt("", 0, j);
					}
				}
			}
		}

		/**
		 * 
		 */
		private void setReputationTabel() {
			// Reputationtable
			for (int i = 0; i < getRowCount(); i++) {
				// the best value for this row
				double maxValue = 0.0;

				// Read the column Header
				JointOperator operator = (JointOperator) this.getValueAt(i, 0);
				// Get The Hashtable with couples
				// <JointOperator,JOintMassDistribution>
				Hashtable<JointOperator, JointMassDistribution> jointMassHash = reputationHashMap
						.get(candidateService);
				// Get the join distribution
				JointMassDistribution jointMass = jointMassHash.get(operator);

				// For each column set the focal element

				for (int j = 1; j < getColumnCount(); j++) {

					String trustElement = getColumnName(j);
					if (trustElement.equals("\u0398")) {
						trustElement = frameOfDiscernment.getUniversalSet()
								.toString();
					} else if (trustElement.equals("\u2205")) {
						trustElement = "{}";
					}
					// Set the cell Joint-Operator/TrustClass
					FocalElement focalElement = (FocalElement) jointMass
							.getFocalElement(trustElement);
					if (focalElement != null) {
						double bpa = focalElement.getBpa();
						if (bpa > maxValue) {
							maxValue = bpa;
							bestValuesIndex[i] = j;
						}
						setValueAt(DoubleUtility.doubleToString(bpa, 2), i, j);
					} else {
						setValueAt("", i, j);
					}
				}
			}
		}

		public Class getColumnClass(int column) {
			return String.class;// getValueAt(0, column).getClass();
		}

		/**
		 * @return the tableType
		 */
		public TableType getTableType() {
			return this.tableType;
		}

		/**
		 * @param tableType
		 *            the tableType to set
		 */
		public void setTableType(TableType tableType) {
			this.tableType = tableType;
		}

		/**
		 * Returns the Column with the best value for the row or -1 it
		 * <code>row</code> is greater than the array length.
		 * 
		 * @param row
		 * @return the column with the best value for the row.
		 */
		public int getBestColumn(int row) {
			if (bestValuesIndex != null && row < bestValuesIndex.length)
				return bestValuesIndex[row];
			else
				return -1;
		}
	}

	private enum TableType {
		REPUTATION("REPUTATION", 1), RELATIONSHIP("RELATIONSHIP", 2), AGGREGATE(
				"AGGREGATE", 3), ONLY_MONITORED("ONLY_MONITORED", 4);
		private String name;
		private int value;

		private TableType(String name, int value) {
			this.name = name;
			this.value = value;
		}

		/**
		 * @return the name
		 */
		public String getName() {
			return this.name;
		}

		/**
		 * @param name
		 *            the name to set
		 */
		public void setName(String name) {
			this.name = name;
		}

		/**
		 * @return the value
		 */
		public int getValue() {
			return this.value;
		}

		/**
		 * @param value
		 *            the value to set
		 */
		public void setValue(int value) {
			this.value = value;
		}
	}

	private class MyCustomTableCellRenderer extends DefaultTableCellRenderer {

		private static final long serialVersionUID = -4395389534866530636L;

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			Component cell = super.getTableCellRendererComponent(table, value,
					isSelected, hasFocus, row, column);

			if (column == ((MyTableModel) table.getModel()).getBestColumn(row)) {
				cell.setBackground(CustomizedColor.MY_OVERLIGHT_GREEN);
				cell.setForeground(Color.black);
			} else {
				cell.setBackground(Color.white);
				cell.setForeground(Color.black);
			}

			// Set the dimension
			table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
			TableColumn col = table.getColumnModel().getColumn(column);
			int width = 0;
			if (column != 0) {
				// Is not the first column
//				if (((String) value).equals(""))
//					width = 35;
//				else
//					width = 50;
				width=35;
			} else {
				width = 65;
			}
			col.setPreferredWidth(width);

			return cell;
		}
	}
}
